#include "tools/log.hpp"
#include "tools/bash.hpp"
#include <filesystem>
#include <iomanip>
#include <sstream>

namespace toolsLib
{
Logger::Logger() : saveLog(false), pureLog(false), setPath(false),
                   enableLogSplit(false), splitDurationMs(0), autoCompress(false)
{
    logFileStartTime = std::chrono::system_clock::now();
}

Logger::~Logger()
{
    if (saveLog) {
        logMutex.lock();
        logFile.close();
        logMutex.unlock();
    }
}

Logger& Logger::getInstance()
{
    static toolsLib::Logger instance;
    return instance;
}

std::map<std::string, long long> timerMap;

std::string Logger::color(int display, int fontcolor, int backgroud)
{
    std::string dis;
    std::string front;
    std::string back;
    switch (display) {
        case 0:
            dis = "0";
            break;
        case 1:
            dis = "1";
            break;
        case 2:
            dis = "4";
            break;
        case 3:
            dis = "5";
            break;
        default:
            dis = "0";
    }
    switch (fontcolor) {
        case 0:
            front = "";
            break;
        case 1:
            front = "30";
            break;  // 黑色
        case 2:
            front = "31";
            break;  // 红色
        case 3:
            front = "32";
            break;  // 绿色
        case 4:
            front = "33";
            break;  // 黄色
        case 5:
            front = "34";
            break;  // 蓝色
        case 6:
            front = "35";
            break;  // 紫色
        case 7:
            front = "36";
            break;  // 青色
        case 8:
            front = "37";
            break;  // 白色
        default:
            front = "";
    }
    switch (backgroud) {
        case 0:
            back = "";
            break;
        case 1:
            back = "40";
            break;  // 黑色
        case 2:
            back = "41";
            break;  // 红色
        case 3:
            back = "42";
            break;  // 绿色
        case 4:
            back = "43";
            break;  // 黄色
        case 5:
            back = "44";
            break;  // 蓝色
        case 6:
            back = "45";
            break;  // 紫色
        case 7:
            back = "46";
            break;  // 青色
        case 8:
            back = "47";
            break;  // 灰色
        default:
            back = "";
    }
    std::string temp;
    if (back == "" && front == "") {
        temp = "\033[" + dis + "m";
    } else if (back == "") {
        temp = "\033[" + dis + ";" + front + "m";
    } else if (front == "") {
        temp = "\033[" + dis + ";" + back + "m";
    } else {
        temp = "\033[" + dis + ";" + front + ";" + back + "m";
    }
    return temp;
}

template <typename T>
std::string Logger::to_string(const T& message)
{
    return std::to_string(message);
}

std::string Logger::to_string(const int& message)
{
    return std::to_string(message);
}

std::string Logger::to_string(const std::string& message)
{
    return message;
}

std::string Logger::to_string(const std::ostringstream& message)
{
    return message.str();
}

std::string Logger::formatFont(const std::string& message,
                               const int display,
                               const int fontcolor,
                               const int backgroud)
{
    return color(display, fontcolor, backgroud) + message + "\033[0m";
}

void Logger::timeCurrently(std::string& time)
{
    auto now = std::chrono::system_clock::now();
    time_t now_time = std::chrono::system_clock::to_time_t(now);
    auto nowInMilliseconds =
        std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
    auto nowInMicroseconds =
        std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()) % 1000;
    auto timestruct = localtime(&now_time);
    std::string year;
    std::string month;
    std::string day;
    std::string hour;
    std::string min;
    std::string sec;
    std::string milSec;
    std::string microSec;
    year = std::to_string(timestruct->tm_year + 1900);
    if (timestruct->tm_mon < 10) {
        month = "0" + std::to_string(timestruct->tm_mon);
    } else {
        month = std::to_string(timestruct->tm_mon);
    }
    if (timestruct->tm_mday < 10) {
        day = "0" + std::to_string(timestruct->tm_mday);
    } else {
        day = std::to_string(timestruct->tm_mday);
    }
    if (timestruct->tm_hour < 10) {
        hour = "0" + std::to_string(timestruct->tm_hour);
    } else {
        hour = std::to_string(timestruct->tm_hour);
    }
    if (timestruct->tm_min < 10) {
        min = "0" + std::to_string(timestruct->tm_min);
    } else {
        min = std::to_string(timestruct->tm_min);
    }
    if (timestruct->tm_sec < 10) {
        sec = "0" + std::to_string(timestruct->tm_sec);
    } else {
        sec = std::to_string(timestruct->tm_sec);
    }
    if (nowInMilliseconds.count() < 10) {
        milSec = "00" + std::to_string(nowInMilliseconds.count());
    } else if (nowInMilliseconds.count() < 100) {
        milSec = "0" + std::to_string(nowInMilliseconds.count());
    } else {
        milSec = std::to_string(nowInMilliseconds.count());
    }
    if (nowInMicroseconds.count() < 10) {
        microSec = "00" + std::to_string(nowInMicroseconds.count());
    } else if (nowInMicroseconds.count() < 100) {
        microSec = "0" + std::to_string(nowInMicroseconds.count());
    } else {
        microSec = std::to_string(nowInMicroseconds.count());
    }
    time = "[" + year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec + ":" +
           milSec + microSec + "]·";
    return;
}

void Logger::timeCurrently(int64_t& time)
{
    auto now = std::chrono::system_clock::now();
    time = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
    return;
}

std::string Logger::getFileName(const std::string& path)
{
    size_t pos = path.find_last_of("/\\");
    return (pos == std::string::npos) ? path : path.substr(pos + 1);
}

bool Logger::timeBuilder(const std::string& funcName, const int& line, const int& timeGap)
{
    // 使用线程安全的静态变量
    static std::map<std::string, int64_t> timerMap;
    static std::mutex timerMutex;
    
    std::string name = funcName + std::to_string(line);
    int64_t currentTime;
    timeCurrently(currentTime);
    
    std::lock_guard<std::mutex> lock(timerMutex);
    
    auto it = timerMap.find(name);
    if (it == timerMap.end()) {
        // 第一次调用，记录时间并允许打印
        timerMap[name] = currentTime;
        return true;
    } else {
        // 检查时间间隔
        int64_t elapsed = currentTime - it->second;
        if (elapsed >= timeGap) {
            // 时间间隔已到，更新时间并允许打印
            it->second = currentTime;
            return true;
        } else {
            // 时间间隔未到，不允许打印
            return false;
        }
    }
}

template <typename T>
void Logger::templateLog(const std::string& file,
                         const std::string& funcName,
                         const int& line,
                         const int& level,
                         const T& message)
{
    std::string nowTime;
    timeCurrently(nowTime);
    std::string result1;
    int localLevel{0};
    if (!pureLog) {
        if (level < 0) {
            localLevel = -level;
            result1 = "[" + funcName + " " + getFileName(file) + ":" + std::to_string(line) + "] ";
        } else {
            localLevel = level;
            result1 = nowTime + "[" + funcName + " " + getFileName(file) + ":" +
                      std::to_string(line) + "] ";
        }
    } else {
        if (level < 0) {
            localLevel = -level;
        } else {
            localLevel = level;
        }
        result1 = "";
    }
    std::string result2 = to_string(message);
    size_t pos = result2.find('\0');
    if (pos != std::string::npos) {
        result2.erase(pos, 1);
    }
    std::string result;

    switch (localLevel) {
        case 0:  // 白色
            result = result1 + result2;
            break;
        case 1:  // 绿色
            result = result1 + formatFont(result2, 0, 3, 0);
            break;
        case 2:  // 黄色
            result = result1 + formatFont(result2, 0, 4, 0);
            break;
        case 3:  // 红色
            result = result1 + formatFont(result2, 0, 2, 0);
            break;
        case 4:  // 蓝色
            result = result1 + formatFont(result2, 0, 5, 0);
            break;
        case 5:  // 紫色
            result = result1 + formatFont(result2, 0, 6, 0);
            break;
        case 6:  // 青色
            result = result1 + formatFont(result2, 0, 7, 0);
            break;
        case 7:  // 灰色
            result = result1 + formatFont(result2, 0, 8, 0);
            break;
        case 8:
            // 白色
            result = result1 + formatFont(result2, 0, 0, 0);
            break;
        default:
            result = result1 + formatFont(result2, 0, 0, 0);
    }
    std::cout << result << std::endl;
    if (saveLog) {
        logMutex.lock();
        // 检查是否需要分割日志
        checkAndSplitLog();
        
        logFile << result1 + result2 << std::endl;
        logMutex.unlock();
    }
    return;
}

template <typename T>
void Logger::templatelogOnce(const std::string& file,
                             const std::string& funcName,
                             const int& line,
                             const int& level,
                             const T& message)
{
    static std::vector<std::string> nameV;
    std::string name = funcName + std::to_string(line);
    auto it = find(nameV.begin(), nameV.end(), name);
    if (it != nameV.end()) {
        return;
    } else {
        log(file, funcName, line, level, message);
        nameV.push_back(name);
        return;
    }
}

template <typename T>
void Logger::templatelogCtrlHz(const std::string& file,
                               const std::string& funcName,
                               const int& line,
                               const int& level,
                               const int& hz,
                               const T& message)
{
    // 将频率（Hz）转换为时间间隔（毫秒）
    // 如果 hz <= 0，则每次都会打印（时间间隔为0）
    int timeGap = 0;
    if (hz > 0) {
        timeGap = 1000 / hz;  // 将Hz转换为毫秒间隔
    }
    
    if (timeBuilder(funcName, line, timeGap)) {
        log(file, funcName, line, level, message);
    }
    // 如果时间间隔未到，直接返回，不打印
}

void Logger::log(const std::string& file,
                 const std::string& funcName,
                 const int& line,
                 const int& level,
                 const std::string& message)
{
    templateLog(file, funcName, line, level, message);
}

void Logger::log(const std::string& file,
                 const std::string& funcName,
                 const int& line,
                 const int& level,
                 const int& message)
{
    templateLog(file, funcName, line, level, message);
}

void Logger::log(const std::string& file,
                 const std::string& funcName,
                 const int& line,
                 const int& level,
                 const std::ostringstream& message)
{
    templateLog(file, funcName, line, level, message);
}

void Logger::log(const std::string& file,
                 const std::string& funcName,
                 const int& line,
                 const int& level,
                 const char* str,
                 ...)
{
    va_list args, args1;
    va_start(args, str);
    va_copy(args1, args);
    std::string formastr(1 + vsnprintf(nullptr, 0, str, args1), '\0');
    va_end(args1);
    vsnprintf(&formastr[0], formastr.size(), str, args);
    va_end(args);
    templateLog(file, funcName, line, level, formastr);
}

void Logger::log(const std::string& file,
                 const std::string& funcName,
                 const int& line,
                 const char* str,
                 ...)
{
    va_list args, args1;
    va_start(args, str);
    va_copy(args1, args);
    std::string formastr(1 + vsnprintf(nullptr, 0, str, args1), '\0');
    va_end(args1);
    vsnprintf(&formastr[0], formastr.size(), str, args);
    va_end(args);
    templateLog(file, funcName, line, 0, formastr);
}

void Logger::logOnce(const std::string& file,
                     const std::string& funcName,
                     const int& line,
                     const int& level,
                     const std::string& message)
{
    templatelogOnce(file, funcName, line, level, message);
}

void Logger::logOnce(const std::string& file,
                     const std::string& funcName,
                     const int& line,
                     const int& level,
                     const int& message)
{
    templatelogOnce(file, funcName, line, level, message);
}

void Logger::logOnce(const std::string& file,
                     const std::string& funcName,
                     const int& line,
                     const int& level,
                     const std::ostringstream& message)
{
    templatelogOnce(file, funcName, line, level, message);
}

void Logger::logOnce(const std::string& file,
                     const std::string& funcName,
                     const int& line,
                     const int& level,
                     const char* str,
                     ...)
{
    va_list args, args1;
    va_start(args, str);
    va_copy(args1, args);
    std::string formastr(1 + vsnprintf(nullptr, 0, str, args1), '\0');
    va_end(args1);
    vsnprintf(&formastr[0], formastr.size(), str, args);
    va_end(args);
    templatelogOnce(file, funcName, line, level, formastr);
}

void Logger::logOnce(const std::string& file,
                     const std::string& funcName,
                     const int& line,
                     const char* str,
                     ...)
{
    va_list args, args1;
    va_start(args, str);
    va_copy(args1, args);
    std::string formastr(1 + vsnprintf(nullptr, 0, str, args1), '\0');
    va_end(args1);
    vsnprintf(&formastr[0], formastr.size(), str, args);
    va_end(args);
    templatelogOnce(file, funcName, line, 0, formastr);
}

void Logger::logCtrlHz(const std::string& file,
                       const std::string& funcName,
                       const int& line,
                       const int& level,
                       const int& hz,
                       const std::string& message)
{
    templatelogCtrlHz(file, funcName, line, level, hz, message);
}

void Logger::logCtrlHz(const std::string& file,
                       const std::string& funcName,
                       const int& line,
                       const int& level,
                       const int& hz,
                       const int& message)
{
    templatelogCtrlHz(file, funcName, line, level, hz, message);
}

void Logger::logCtrlHz(const std::string& file,
                       const std::string& funcName,
                       const int& line,
                       const int& level,
                       const int& hz,
                       const std::ostringstream& message)
{
    templatelogCtrlHz(file, funcName, line, level, hz, message);
}

void Logger::logCtrlHz(const std::string& file,
                       const std::string& funcName,
                       const int& line,
                       const int& level,
                       const int& hz,
                       const char* str,
                       ...)
{
    va_list args, args1;
    va_start(args, str);
    va_copy(args1, args);
    std::string formastr(1 + vsnprintf(nullptr, 0, str, args1), '\0');
    va_end(args1);
    vsnprintf(&formastr[0], formastr.size(), str, args);
    va_end(args);
    templatelogCtrlHz(file, funcName, line, level, hz, formastr);
}

void Logger::logCtrlHz(const std::string& file,
                       const std::string& funcName,
                       const int& line,
                       const int& hz,
                       const char* str,
                       ...)
{
    va_list args, args1;
    va_start(args, str);
    va_copy(args1, args);
    std::string formastr(1 + vsnprintf(nullptr, 0, str, args1), '\0');
    va_end(args1);
    vsnprintf(&formastr[0], formastr.size(), str, args);
    va_end(args);
    templatelogCtrlHz(file, funcName, line, 0, hz, formastr);
}

void Logger::setLogPath(std::string& path)
{
    if (!setPath) {
        if (path.empty()) {
            std::cout << formatFont("日志文件为空！", 0, 2, 0);
            return;
        }
        setPath = true;
        baseLogPath = path;  // 保存基础路径
        oldPath.assign(path);
        logFile.open(path.c_str(), std::ios::app);
        logFileStartTime = std::chrono::system_clock::now();  // 记录文件开始时间
    } else {
        if (path == oldPath) {
            return;
        } else {
            logFile.close();
            baseLogPath = path;  // 更新基础路径
            oldPath = path;
            logMutex.lock();
            logFile.open(path.c_str(), std::ios::app);
            logMutex.unlock();
            logFileStartTime = std::chrono::system_clock::now();  // 更新文件开始时间
        }
    }

    if (!logFile) {
        std::cout << formatFont("无法打开日志文件！", 0, 2, 0);
        return;
    }

    saveLogMutex.lock();
    saveLog = true;
    saveLogMutex.unlock();

    return;
}

void Logger::setLogPath(const char* path)
{
    std::string toString;
    toString.assign(path);
    setLogPath(toString);
}

void Logger::setPureLog(const bool pure)
{
    pureLog = pure;
}

void Logger::setSaveLog(const bool save)
{
    saveLogMutex.lock();
    saveLog = save;
    saveLogMutex.unlock();
}

void Logger::setLogSplit(bool enable)
{
    std::lock_guard<std::mutex> lock(saveLogMutex);
    enableLogSplit = enable;
    if (enable && !baseLogPath.empty()) {
        // 如果启用分割且已有基础路径，更新当前文件开始时间
        logFileStartTime = std::chrono::system_clock::now();
    }
}

void Logger::setLogSplitByTime(int64_t durationMs)
{
    std::lock_guard<std::mutex> lock(saveLogMutex);
    splitDurationMs = durationMs;
    if (durationMs > 0 && !baseLogPath.empty()) {
        // 如果设置了时间分割且已有基础路径，更新当前文件开始时间
        logFileStartTime = std::chrono::system_clock::now();
    }
}

void Logger::setLogAutoCompress(bool enable)
{
    std::lock_guard<std::mutex> lock(saveLogMutex);
    autoCompress = enable;
}

void Logger::splitLog()
{
    if (!saveLog || !enableLogSplit) {
        return;
    }

    std::lock_guard<std::mutex> lock(logMutex);
    
    if (!logFile.is_open()) {
        return;
    }

    // 关闭当前日志文件
    logFile.close();

    // 保存旧文件路径用于压缩
    std::string oldFilePath = oldPath;

    // 生成新的日志文件名
    std::string newPath = generateLogFileName(baseLogPath);
    oldPath = newPath;
    logFileStartTime = std::chrono::system_clock::now();

    // 打开新文件
    logFile.open(newPath.c_str(), std::ios::app);
    if (!logFile) {
        std::cout << formatFont("无法打开新的日志文件！", 0, 2, 0);
    }

    // 压缩旧文件（如果启用，在锁外执行以避免阻塞）
    if (autoCompress && !oldFilePath.empty()) {
        logMutex.unlock();
        compressLogFile(oldFilePath);
        logMutex.lock();
    }
}

std::string Logger::generateLogFileName(const std::string& basePath)
{
    if (basePath.empty()) {
        return basePath;
    }

    // 获取当前时间
    auto now = std::chrono::system_clock::now();
    auto time_t = std::chrono::system_clock::to_time_t(now);
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()) % 1000;

    std::tm* tm_info = std::localtime(&time_t);
    
    // 格式化时间戳：YYYYMMDD_HHMMSS_mmm
    std::ostringstream oss;
    oss << std::put_time(tm_info, "%Y%m%d_%H%M%S");
    oss << "_" << std::setfill('0') << std::setw(3) << ms.count();

    std::string timestamp = oss.str();

    // 分离文件名和扩展名
    namespace fs = std::filesystem;
    fs::path path(basePath);
    std::string stem = path.stem().string();
    std::string extension = path.extension().string();
    std::string parent = path.parent_path().string();

    // 生成新文件名：原文件名_时间戳.扩展名
    std::string newFileName;
    if (parent.empty() || parent == ".") {
        newFileName = stem + "_" + timestamp + extension;
    } else {
        newFileName = parent + "/" + stem + "_" + timestamp + extension;
    }

    return newFileName;
}

void Logger::compressLogFile(const std::string& filePath)
{
    if (filePath.empty()) {
        return;
    }

    namespace fs = std::filesystem;
    if (!fs::exists(filePath)) {
        return;
    }

    // 使用 Bash 的 gzip 功能压缩
    toolsLib::Bash::getInstance().gzip(filePath);
}

void Logger::checkAndSplitLog()
{
    // 注意：此函数应在持有 logMutex 锁的情况下调用
    if (!saveLog || !enableLogSplit) {
        return;
    }

    // 检查时间分割
    if (splitDurationMs > 0) {
        auto now = std::chrono::system_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
            now - logFileStartTime).count();
        
        if (elapsed >= splitDurationMs) {
            // 需要先释放锁，调用 splitLog（内部会获取锁），然后重新获取锁
            logMutex.unlock();
            splitLog();
            logMutex.lock();
        }
    }
}

void Logger::unpolishedLog(int level, std::string& part1, std::string& part2, std::string& part3)
{
    std::string fontPart1;
    std::string fontPart2;
    std::string fontPart3;
    switch (level) {
        case 0:
            fontPart1 = part1;
            fontPart2 = part2;
            fontPart3 = part3;
            break;
        case 1:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 3, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        case 2:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 4, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        case 3:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 2, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        case 4:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 5, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        case 5:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 6, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        case 6:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 7, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        case 7:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 8, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        case 8:
            fontPart1 = formatFont(part1, 0, 0, 0);
            fontPart2 = formatFont(part2, 0, 0, 0);
            fontPart3 = formatFont(part3, 0, 0, 0);
            break;
        default:
            fontPart1 = part1;
            fontPart2 = part2;
            fontPart3 = part3;
            break;
    }
    if (saveLog) {
        logMutex.lock();
        // 检查是否需要分割日志
        checkAndSplitLog();
        
        logFile << part1 << part2 << part3 << std::endl;
        logMutex.unlock();
    }
    std::cout << fontPart1 << fontPart2 << fontPart3 << std::endl;
}

void Logger::unpolishedLog(int level, std::string& log)
{
    std::string fontPart;
    switch (level) {
        case 0:
            fontPart = log;
            break;
        case 1:
            fontPart = formatFont(log, 0, 3, 0);
            break;
        case 2:
            fontPart = formatFont(log, 0, 4, 0);
            break;
        case 3:
            fontPart = formatFont(log, 0, 2, 0);
            break;
        case 4:
            fontPart = formatFont(log, 0, 5, 0);
            break;
        case 5:
            fontPart = formatFont(log, 0, 6, 0);
            break;
        case 6:
            fontPart = formatFont(log, 0, 7, 0);
            break;
        case 7:
            fontPart = formatFont(log, 0, 8, 0);
            break;
        case 8:
            fontPart = formatFont(log, 0, 0, 0);
            break;
        default:
            fontPart = log;
            break;
    }
    if (saveLog) {
        logMutex.lock();
        // 检查是否需要分割日志
        checkAndSplitLog();
        
        logFile << log << std::endl;
        logMutex.unlock();
    }
    std::cout << fontPart << std::endl;
}

}  // namespace toolsLib
